# 1. 如何为函数定义keyword-only参数（写出个例子即可）？
# *或*args后面的都是keyword-only参数
# def fn(*args,x,y):
#     pass
# def fn1(*,x,y):
#     pass
#  x和y都是keyword-only参数
# 2. 什么是LEGB，请解释
# 变量名解析原则LEGB，按照以下顺序查找解析变量
# L:Local 本地变量
# E:Enclosing 闭包原理中的变量
# G:Global 全局变量
# B:Build-in 内置变量

# 3. 使用本周学习的技术改造第一周的计算器实现，其目标如下：
#
#    1. 运行后提示让用户输入一个数字
#    2. 提示输入操作符（+ - * /）
#    3. 再次提示输入一个数字
#    4. 打印计算结果
#    5. 在不退出程序的前提下，可以允许用户继续输入新一组数据计
#    6. 尽可能改善用户体验（新需求）

def runcalculator():
    """计算器"""

    def singleoperationmode():
        """逐个获取数字和运算符"""

        first_num = input('请输入一个数字：')
        sign = str(input('请输入运算符（+、-、*、/）：'))
        second_num = input('请再输入一个数字：')
        formula = first_num + sign + second_num
        return formula

    def multipleoperationmode():
        """直接获取计算公式"""
        formula = input('请输入计算公式[例：1+2*(3+4)]：')
        return formula

    runtag = 'Y'
    mode = ''

    while runtag.upper() == 'Y':
        if mode:
            if input('切换模式？Y/N: ') == 'Y':
                mode = input('请选择模式(S：简单模式)(M：多则运算模式): ')
        else:
            mode = input('请选择模式(S：简单模式)(M：多则运算模式): ')

        if mode.upper() == 'S':
            formula = singleoperationmode()
        elif mode.upper() == 'M':
            formula = multipleoperationmode()
        else:
            print('wrong mode input.')
            runtag = input('Continue? Y/N: ')
            continue

        # 将计算公式转换为列表处理
        formulalist = transformatformula(formula)

        if not formulalist:
            runtag = input('Continue? Y/N: ')
            continue

        # 计算公式
        result = calculateformula(formulalist)

        if result == 'Error':
            runtag = input('Continue? Y/N: ')
            continue

        # 返回结果
        print(formula + '=' + str(result))

        runtag = input('Continue? Y/N: ')

def transformatformula(formula):
    """将一串字符处理成列表以供计算"""

    # 可识别的符号
    symbols = {'+', '-', '*', '/', '(', ')'}
    errsymbol = []
    number = ''
    numbers = []

    # 将数字和运算符分离保存到列表中
    for value in formula:
        if value.isnumeric():
            number += value
        else:
            if value in symbols:
                if number:
                    numbers.append(number)
                    number = ''
                numbers.append(value)
            elif value == '.':
                number += value
            else:
                #若字符串中包含未识别的符号则抛异常
                errsymbol.append(value)

    # 如果number不为空说明最后一个数字还未保存
    if number:
        numbers.append(number)

    if errsymbol:
        numbers.clear()
        print(f'formula has illegal symbol: {errsymbol}.')

    return numbers

def calculateformula(formulalist):
    """计算一段公式返回结果"""

    # 计算括号内的内容
    while '(' in formulalist:
        formulalist = calculatebracket(formulalist)

    # 乘除计算
    while '*' in formulalist or '/' in formulalist:
        formulalist = advancecalculate(formulalist)

    # 加减计算
    result = advancecalculate(formulalist, False)

    if formulalist:
        if float(result[0]) % 1 == 0:
            result = int(float(result[0]))
        else:
            result = float(result[0])
    else:
        result = 'Error'

    return result

def calculatebracket(formulalist):
    """按优先度计算括号内的公式"""
    # 获取括号所在的索引
    bracketindex = findbracket(formulalist)

    # 括号左右不能为数字，否则抛异常返回空列表
    if bracketindex[0] > 0 and bracketindex[0] < len(formulalist) - 1:
        if formulalist[bracketindex[0] - 1].isnumeric() or formulalist[bracketindex[1] + 1].isnumeric():
            print('Please add an operator between a number and ().')
            formulalist.clear()
            return formulalist

    # 切片获取括号内容
    bracketformula = formulalist[bracketindex[0] + 1: bracketindex[1]]

    # 计算括号内的公式并将结果替换到原始公式列表中
    result = calculateformula(bracketformula)
    if result == 'Error':
        formulalist.clear()
    else:
        formulalist[bracketindex[0]: bracketindex[1] + 1] = ' '
        formulalist[bracketindex[0]] = str(result)

    return formulalist

def findbracket(formulalist):
    """返回优先度最高的括号索引值"""

    leftbrackets = []
    rightbrackets = []
    bracketindex = 0

    # 查找并记录所有括号的索引值
    for value in formulalist:
        bracketindex += 1
        if value == '(':
            leftbrackets.append(bracketindex)
        if value == ')':
            rightbrackets.append(bracketindex)

    leftbrackets.sort(reverse=True)

    # 第一个右括号的优先度最高
    endindex = rightbrackets[0] - 1

    # 找到索引值小于右括号的第一个左括号
    for leftbracket in leftbrackets:
        if leftbracket < endindex:
            startindex = leftbracket - 1
            break

    return [startindex, endindex]

def calculate(formulalist):
    """四则运算"""
    errmsg = ''

    first_num = float(formulalist[0])
    sign = formulalist[1]
    second_num = float(formulalist[2])

    try:
        if sign == '+':
            result = first_num + second_num
        elif sign == '-':
            result = first_num - second_num
        elif sign == '*':
            result = first_num * second_num
        elif sign == '/':
            result = first_num / second_num
    except ValueError as err:
        errmsg = f'类型错误: {err}'
    except ZeroDivisionError as err:
        errmsg = f'除数不能为0: {err}'

    if errmsg:
        result = 'Error'
        print(errmsg)

    return result

def advancecalculate(formulalist, advance = True):
    """按优先度计算"""

    if advance:
        operator = ['*', '/']
    else:
        operator = ['+', '-']

    # 遍历公式内的运算符，计算并得出结果替换到原始列表
    for i in range(0,2):
        while operator[i] in formulalist:
            operatorindex = formulalist.index(operator[i])
            calculatelist = formulalist[operatorindex - 1: operatorindex + 2]
            result = calculate(calculatelist)
            if result == 'Error':
                formulalist.clear()
                break
            formulalist[operatorindex - 1: operatorindex + 2] = ' '
            formulalist[operatorindex - 1] = str(result)

    # 返回计算后的公式列表
    return formulalist

runcalculator()

